package com.maschinenstuermer.profiler.transform;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

import org.ow2.util.asm.MethodVisitor;
import org.ow2.util.asm.Type;
import org.ow2.util.asm.commons.AdviceAdapter;


class ConnectionAdviceAdapter extends AdviceAdapter {
	private static final String MAP_CLAZZ =
		Type.getInternalName(Map.class);
	private static final Method PUT_METHOD;
	private static final String PUT_METHOD_DESC;
	
	private static final String JDBC_TRACING_CONSTANTS_CLAZZ = 
		Type.getInternalName(JdbcTracingConstants.class);
	private static final Field PREPARED_STATEMENT_MAP_FIELD;
	private static final String PREPARED_STATEMENT_MAP_FIELD_DESC;
	
	static {
		try {
			PUT_METHOD = Map.class.getDeclaredMethod("put", Object.class, Object.class);
			PUT_METHOD_DESC = Type.getMethodDescriptor(PUT_METHOD);
				
			PREPARED_STATEMENT_MAP_FIELD = JdbcTracingConstants.class.getDeclaredField("PREPARED_STATEMENT_MAP");
			PREPARED_STATEMENT_MAP_FIELD_DESC = Type.getDescriptor(PREPARED_STATEMENT_MAP_FIELD.getType());
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}
	
	public ConnectionAdviceAdapter(final MethodVisitor mv, final int access,
			final String name, final String desc) {
		super(mv, access, name, desc);
	}

	protected void onMethodExit(final int opcode) {
		if (opcode == ARETURN) {
			dup(); // duplicate return value
			mv.visitFieldInsn(GETSTATIC, JDBC_TRACING_CONSTANTS_CLAZZ, 
					PREPARED_STATEMENT_MAP_FIELD.getName(), PREPARED_STATEMENT_MAP_FIELD_DESC); // put map on stack
			swap(); // swap return with map
			mv.visitVarInsn(ALOAD, 1); // ALOAD first param containg sql statement
			mv.visitMethodInsn(INVOKEINTERFACE, MAP_CLAZZ, 
					PUT_METHOD.getName(), PUT_METHOD_DESC);
			mv.visitInsn(POP);
		}
	}
}
